/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package ecodex.serviciospruebas;

import ecodex.restapi.RestApi;
import ecodex.restapi.models.CertificadoClave;
import ecodex.restapi.models.DocumentoInfo;
import ecodex.restapi.models.Token;
import ecodex.sellado.EcodexSellado;
import ecodex.servicios.cancelacion.*;
import ecodex.servicios.clientes.*;
import ecodex.servicios.seguridad.*;
import ecodex.servicios.timbrado.*;
import static ecodex.serviciospruebas.frames.JFrameServicios.RFC_Emisor_Pruebas;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.xml.sax.SAXException;

/**
 *
 * @author Ecodex6
 */
public class Servicios {
    private final String Integrador_ID;
    private final String Alta_Emisores_ID;
    private final String RFC_Integrador_Pruebas;
    private final String Url_Carga_Certificados;
    
    public Servicios(){
        //Utils Utilidades = new Utils();
        Integrador_ID = Utils.getPropertyValue("INTEGRADOR_ID");
        Alta_Emisores_ID = Utils.getPropertyValue("ALTA_EMISORES_ID");
        RFC_Integrador_Pruebas = Utils.getPropertyValue("RFC_INTEGRADOR_PRUEBAS");
        Url_Carga_Certificados = Utils.getPropertyValue("URL_CARGA_CERTIFICADOS");
    }    

    /**
     * Realiza el timbrado del archivo XML
     * @param rfcEmisor RFC del Emisor
     * @param xmlFileName   Cadena conteniendo el archivo XML
     * @return Una string con el resultado de la operacion
     * @throws IOException 
     * @throws javax.xml.parsers.ParserConfigurationException 
     * @throws org.xml.sax.SAXException 
     * @throws java.io.UnsupportedEncodingException 
     * @throws javax.xml.xpath.XPathExpressionException 
     */
    public String timbrarComprobante(String rfcEmisor, String xmlFileName) 
            throws IOException, ParserConfigurationException, SAXException, UnsupportedEncodingException, XPathExpressionException {

        Long trsId = System.currentTimeMillis();       
        String comprobanteTimbrado;
        ecodex.servicios.timbrado.ObjectFactory factory = new ecodex.servicios.timbrado.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación
        String token = obtenerToken(rfcEmisor, trsId);

        // Sellado del XML
        String cadenaXML = EcodexSellado.sellarXML(xmlFileName, true);        

        // Se crea el Comprobante
        ComprobanteXML comprobante = new ComprobanteXML();
        comprobante.setDatosXML(factory.createComprobanteXMLDatosXML(cadenaXML));

        // Se crea la Solicitud para el Timbrado
        SolicitudTimbraXML solicitud = new SolicitudTimbraXML();
        solicitud.setRFC(factory.createSolicitudTimbraXMLRFC(rfcEmisor));
        solicitud.setToken(factory.createSolicitudTimbraXMLToken(token));
        solicitud.setTransaccionID(trsId);
        solicitud.setComprobanteXML(factory.createComprobanteXML(comprobante));

        // Intenta el Timbrado
        RespuestaTimbraXML respuesta;
        try {
            
            Timbrado_Service servicio = new Timbrado_Service();
            Timbrado puerto = servicio.getPuertoTimbrado();
            respuesta = puerto.timbraXML(solicitud);
            comprobanteTimbrado = respuesta.getComprobanteXML().getValue().getDatosXML().getValue();
            
        } catch (TimbradoTimbraXMLFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaServicio falla = ex.getFaultInfo();
            comprobanteTimbrado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (TimbradoTimbraXMLFallaSesionFaultFaultMessage ex) {
            comprobanteTimbrado =  ex.getMessage();
        } catch (TimbradoTimbraXMLFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaValidacion falla = ex.getFaultInfo();
            comprobanteTimbrado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        }

        System.out.println("Comprobante Timbrado: " + comprobanteTimbrado);
        return comprobanteTimbrado;
    }    

    /**
     * Obtiene el estatus actual de un comprobante
     * @param rfcEmisor RFC del Emisor
     * @param uuid UUID generado al timbrar
     * @param transaccionIdOriginal Id de la transacción devuelta cuando se realizó el timbrado
     * @return Una string con el estatus del comprobante
     */
    public String obtenerEstatusComprobante(String rfcEmisor, String uuid, long transaccionIdOriginal) {

        Long trsId = System.currentTimeMillis();
        String estatusTimbrado;
        ecodex.servicios.timbrado.ObjectFactory factory = new ecodex.servicios.timbrado.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación        
        String token = obtenerToken(rfcEmisor, trsId);
        
        // Se crea la Solicitud para el Estatus del Timbrado
        SolicitudEstatusTimbrado solicitud = new SolicitudEstatusTimbrado();
        solicitud.setRFC(factory.createSolicitudEstatusTimbradoRFC(rfcEmisor));
        solicitud.setToken(factory.createSolicitudEstatusTimbradoToken(token));
        solicitud.setTransaccionID(trsId);
        solicitud.setUUID(factory.createSolicitudEstatusTimbradoUUID(uuid));
        solicitud.setTransaccionOriginal(transaccionIdOriginal);

        // Intenta obtener el estatus
        RespuestaEstatusTimbrado respuesta;
        try {
            Timbrado_Service servicio = new Timbrado_Service();
            Timbrado puerto = servicio.getPuertoTimbrado();
            respuesta = puerto.estatusTimbrado(solicitud);
            estatusTimbrado = String.format("Codigo: %s\nDescripción: %s", 
                                            respuesta.getEstatus().getValue().getCodigo().getValue(), 
                                            respuesta.getEstatus().getValue().getDescripcion().getValue());
        } catch (TimbradoEstatusTimbradoFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaServicio falla = ex.getFaultInfo();
            estatusTimbrado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (TimbradoEstatusTimbradoFallaSesionFaultFaultMessage ex) {
            estatusTimbrado = ex.getMessage();
        } catch (TimbradoEstatusTimbradoFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaValidacion falla = ex.getFaultInfo();
            estatusTimbrado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        }

        System.out.println(estatusTimbrado);
        return estatusTimbrado;
    }
    
    /**
     * Recupera el XML timbrado
     * @param rfcEmisor RFC del Emisor
     * @param uuid UUID generado al timbrar
     * @param transaccionIdOriginal Id de la transacción devuelta cuando se realizó el timbrado
     * @return Una string con el XML timbrado
     */
    public String obtenerXMLComprobante(String rfcEmisor, String uuid, long transaccionIdOriginal) {

        Long trsId = System.currentTimeMillis();
        String xmlTimbrado;
        ecodex.servicios.timbrado.ObjectFactory factory = new ecodex.servicios.timbrado.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación        
        String token = obtenerToken(rfcEmisor, trsId);        
        
        // Se crea la Solicitud para el obtener el XML
        SolicitudObtenerTimbrado solicitud = new SolicitudObtenerTimbrado();
        solicitud.setRFC(factory.createSolicitudObtenerTimbradoRFC(rfcEmisor));
        solicitud.setToken(factory.createSolicitudObtenerTimbradoToken(token));
        solicitud.setTransaccionID(trsId);
        solicitud.setUUID(factory.createSolicitudObtenerQRTimbradoUUID(uuid));
        solicitud.setTransaccionOriginal(transaccionIdOriginal);

        // Intenta obtener el XML
        RespuestaObtenerTimbrado respuesta;
        try {
            Timbrado_Service servicio = new Timbrado_Service();
            Timbrado puerto = servicio.getPuertoTimbrado();
            respuesta = puerto.obtenerTimbrado(solicitud);
            xmlTimbrado = respuesta.getComprobanteXML().getValue().getDatosXML().getValue();
        } catch (TimbradoObtenerTimbradoFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaServicio falla = ex.getFaultInfo();
            xmlTimbrado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (TimbradoObtenerTimbradoFallaSesionFaultFaultMessage ex) {
            xmlTimbrado = ex.getMessage();
        } catch (TimbradoObtenerTimbradoFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaValidacion falla = ex.getFaultInfo();
            xmlTimbrado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        }

        System.out.println("XML Timbrado: " + xmlTimbrado);
        return xmlTimbrado;
    }
    
    /**
     * Cancela varios comprobantes
     * @param rfcEmisor RFC del Emisor
     * @param uuids Arreglo de UUIDs generados al timbrar
     * @return Una string con el resultado de la cancelación
     */
    public String cancelarMultipleComprobante(String rfcEmisor, String[] uuids) {

        Long trsId = System.currentTimeMillis();
        String comprobanteCancelado = "";
        String separador = "----------------------------------------";
        ecodex.servicios.cancelacion.ObjectFactory factory = new ecodex.servicios.cancelacion.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación
        String token = obtenerToken(rfcEmisor, trsId);

        // Crea la lista de UUIDs a cancelar
        ListaCancelar lista = factory.createListaCancelar();
        lista.getGuid().addAll(Arrays.asList(uuids));        

        // Se crea la Solicitud para la Cancelación
        SolicitudCancelaMultiple solicitud = new SolicitudCancelaMultiple();
        solicitud.setRFC(factory.createSolicitudCancelaMultipleRFC(rfcEmisor));
        solicitud.setToken(factory.createSolicitudCancelaMultipleToken(token));
        solicitud.setTransaccionID(trsId);
        solicitud.setListaCancelar(factory.createSolicitudCancelaMultipleListaCancelar(lista));

        // Intenta cancelar
        RespuestaCancelaMultiple respuesta;
        try {
            Cancelaciones_Service servicio = new Cancelaciones_Service();
            Cancelaciones puerto = servicio.getPuertoCancelacion();
            respuesta = puerto.cancelaMultiple(solicitud);
            for (ResultadoCancelacion resultado : respuesta.getResultado().getValue().getResultadoCancelacion()) {
                comprobanteCancelado += String.format("UUID : %s\nEstatus : %s\n%s\n", resultado.getUUID(), resultado.getEstatus().getValue(), separador);
            }            

        } catch (CancelacionesCancelaMultipleFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.cancelacion.FallaServicio falla = ex.getFaultInfo();
            comprobanteCancelado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (CancelacionesCancelaMultipleFallaSesionFaultFaultMessage ex) {
            comprobanteCancelado = ex.getMessage();
        }

        System.out.println(comprobanteCancelado);
        return comprobanteCancelado;
    }
    
    /**
     * Cancela un comprobante mediante el servicio de timbrado
     * @param rfcEmisor
     * @param uuid
     * @return
     * @throws TimbradoCancelaTimbradoFallaServicioFaultFaultMessage
     * @throws TimbradoCancelaTimbradoFallaSesionFaultFaultMessage
     * @throws TimbradoCancelaTimbradoFallaValidacionFaultFaultMessage 
     */
    public boolean cancelarComprobante(String rfcEmisor, String uuid) 
            throws TimbradoCancelaTimbradoFallaServicioFaultFaultMessage, TimbradoCancelaTimbradoFallaSesionFaultFaultMessage, TimbradoCancelaTimbradoFallaValidacionFaultFaultMessage {

        Long trsId = System.currentTimeMillis();
        boolean cancelada = false;
        ecodex.servicios.timbrado.ObjectFactory factory = new ecodex.servicios.timbrado.ObjectFactory();
        
        // Token de seguridad para permitir la operación
        String token = obtenerToken(rfcEmisor, trsId);     

        // Solicitud para la Cancelación
        SolicitudCancelaTimbrado solicitud = new SolicitudCancelaTimbrado();
        solicitud.setRFC(factory.createSolicitudCancelaTimbradoRFC(rfcEmisor));                
        solicitud.setToken(factory.createSolicitudCancelaTimbradoToken(token));
        solicitud.setTransaccionID(trsId);
        solicitud.setUUID(factory.createSolicitudCancelaTimbradoUUID(uuid));

        // Intenta cancelar
        RespuestaCancelaTimbrado respuesta;
        try {
            Timbrado_Service servicio = new Timbrado_Service();
            Timbrado puerto = servicio.getPuertoTimbrado();
            respuesta = puerto.cancelaTimbrado(solicitud);
            cancelada = respuesta.isCancelada();
        } catch (TimbradoCancelaTimbradoFallaServicioFaultFaultMessage | 
                TimbradoCancelaTimbradoFallaSesionFaultFaultMessage | 
                TimbradoCancelaTimbradoFallaValidacionFaultFaultMessage ex) {
            Logger.getLogger(Servicios.class.getName()).log(Level.SEVERE, null, ex);
            throw ex;
        }
        
        return cancelada;
    }    
    
    /**
     * Cancela un comprobante generado por otro PAC
     * @param rfcEmisor RFC del Emisor
     * @param rfcReceptor RFC del Receptor
     * @param uuid UUID generado al timbrar
     * @return 
     */
    public String cancelarOtrosComprobante(String rfcEmisor, String rfcReceptor, String uuid) {

        Long trsId = System.currentTimeMillis();
        String comprobanteCancelado;
        ecodex.servicios.cancelacion.ObjectFactory factory = new ecodex.servicios.cancelacion.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación
        String token = obtenerToken(rfcEmisor, trsId);

        // Se crea la Solicitud para la Cancelación
        SolicitudCancelaOtros solicitud = new SolicitudCancelaOtros();
        solicitud.setRFCEmisor(factory.createSolicitudCancelaOtrosRFCEmisor(rfcEmisor));
        solicitud.setRFCReceptor(factory.createSolicitudCancelaOtrosRFCReceptor(rfcReceptor));
        solicitud.setToken(factory.createSolicitudCancelaOtrosToken(token));
        solicitud.setUUID(uuid);
        solicitud.setTransaccionID(trsId);

        // Intenta cancelar
        RespuestaCancelaOtros respuesta;
        try {
            Cancelaciones_Service servicio = new Cancelaciones_Service();
            Cancelaciones puerto = servicio.getPuertoCancelacion();
            respuesta = puerto.cancelaOtros(solicitud);
            ResultadoCancelacion resultado = respuesta.getResultado().getValue().getResultadoCancelacion().get(0);
            comprobanteCancelado = String.format("UUID : %s\nEstatus : %s", resultado.getUUID(), resultado.getEstatus().getValue());
            
        } catch (CancelacionesCancelaOtrosFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.cancelacion.FallaServicio falla = ex.getFaultInfo();
            comprobanteCancelado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());            
        } catch (CancelacionesCancelaOtrosFallaSesionFaultFaultMessage ex) {
            comprobanteCancelado = ex.getMessage();
        } catch (CancelacionesCancelaOtrosFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.cancelacion.FallaValidacion falla = ex.getFaultInfo();
            comprobanteCancelado =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());            
        }

        System.out.println(comprobanteCancelado);
        return comprobanteCancelado;
    }
    
    /**
     * Obtiene el XML del acuse de cancelación
     * @param rfcEmisor RFC del Emisor
     * @param uuid UUID generado al timbrar
     * @return Una string con el XML del acuse de cancelación
     */
    public String obtenerAcuseCancelacionComprobante(String rfcEmisor, String uuid)  {

        Long trsId = System.currentTimeMillis();
        String estatusAcuse;
        ecodex.servicios.cancelacion.ObjectFactory factory = new ecodex.servicios.cancelacion.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación        
        String token = obtenerToken(rfcEmisor, trsId);   
        
        // Se crea la Solicitud para obtener el Acuse de Cancelación
        SolicitudAcuse solicitud = new SolicitudAcuse();
        solicitud.setRFC(factory.createSolicitudAcuseRFC(rfcEmisor));
        solicitud.setToken(factory.createSolicitudAcuseToken(token));
        solicitud.setTransaccionID(trsId);
        solicitud.setUUID(uuid);

        // Intenta obtener el acuse
        RespuestaRecuperarAcuse respuesta;
        try {
            Cancelaciones_Service servicio = new Cancelaciones_Service();
            Cancelaciones puerto = servicio.getPuertoCancelacion();
            respuesta = puerto.recuperarAcuses(solicitud);
            estatusAcuse = String.format("Estatus: %s\nAcuse XML:\n%s", respuesta.getEstatus().getValue(), respuesta.getAcuseXML().getValue());
        } catch (CancelacionesRecuperarAcusesFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.cancelacion.FallaServicio falla = ex.getFaultInfo();
            estatusAcuse =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());            
        } catch (CancelacionesRecuperarAcusesFallaSesionFaultFaultMessage ex) {
            estatusAcuse = ex.getMessage();
        } catch (CancelacionesRecuperarAcusesFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.cancelacion.FallaValidacion falla = ex.getFaultInfo();
            estatusAcuse =  String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        }

        System.out.println(estatusAcuse);
        return estatusAcuse;
    }
    
    /**
     * Genera la imagen QR del comprobante
     * @param rfcEmisor RFC del Emisor
     * @param uuid UUID generado al timbrar
     * @return Devuelve la imagen QR del comprobante
     */
    public Icon obtenerQRComprobante(String rfcEmisor, String uuid) {
        
        Icon qr = null;
        Long trsId = System.currentTimeMillis();        
        String bitmapString;
        ecodex.servicios.timbrado.ObjectFactory factory = new ecodex.servicios.timbrado.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación        
        String token = obtenerToken(rfcEmisor, trsId);          
        
        // Se crea la Solicitud para obtener la imagen QR
        SolicitudObtenerQRTimbrado solicitud = new SolicitudObtenerQRTimbrado();
        solicitud.setRFC(factory.createSolicitudObtenerQRTimbradoRFC(rfcEmisor));
        solicitud.setToken(factory.createSolicitudObtenerQRTimbradoToken(token));
        solicitud.setUUID(factory.createSolicitudObtenerQRTimbradoUUID(uuid));
        solicitud.setTransaccionID(trsId);
        
        // Intenta obtener la imagen
        RespuestaObtenerQRTimbrado respuesta;
        try {
            Timbrado_Service servicio = new Timbrado_Service();
            Timbrado puerto = servicio.getPuertoTimbrado();
            respuesta = puerto.obtenerQRTimbrado(solicitud);
            byte[] qrBitmap = respuesta.getQR().getValue().getImagen().getValue();
            BufferedImage imageQR = ImageIO.read(new ByteArrayInputStream(qrBitmap));
            qr = new ImageIcon(imageQR);
            bitmapString = Utils.bytesToHex(qrBitmap);
        } catch (TimbradoObtenerQRTimbradoFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaServicio falla = ex.getFaultInfo();
            bitmapString = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (TimbradoObtenerQRTimbradoFallaSesionFaultFaultMessage ex) {
            bitmapString = ex.getMessage();
        } catch (TimbradoObtenerQRTimbradoFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.timbrado.FallaValidacion falla = ex.getFaultInfo();
            bitmapString = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        }
        catch (IOException ex) {
            Logger.getLogger(Servicios.class.getName()).log(Level.SEVERE, null, ex);
            bitmapString = ex.getMessage();
        }

        System.out.println("Imagen QR: " + bitmapString);
        return qr;
    }
    
    /**
     * Genera un nuevo Emisor
     * @param rfcEmisor RFC del nuevo Emisor
     * @param razonSocial Razón Social del nuevo Emisor
     * @param email Correo del nuevo Emisor
     * @return Una string con el resultado de la operación.
     */
    public String agregarEmisor(String rfcEmisor, String razonSocial, String email) {
        String infoEmisor;
        Long trsId = System.currentTimeMillis();        
        ecodex.servicios.clientes.ObjectFactory factory = new ecodex.servicios.clientes.ObjectFactory();
        
        // Obtenemos el token de seguridad para permitir la operación
        String token = obtenerTokenAltaEmisor(trsId);

        // Crea el objeto Emisor
        AltaEmisor emisor = factory.createAltaEmisor();
        emisor.setCorreoElectronico(factory.createAltaEmisorCorreoElectronico(email));
        emisor.setRFC(factory.createAltaEmisorRFC(rfcEmisor)); 
        emisor.setRazonSocial(factory.createAltaEmisorRazonSocial(razonSocial));

        // Crea la solicitud para dar de alta el nuevo Emisor
        SolicitudRegistroCliente solicitud = factory.createSolicitudRegistroCliente();
        solicitud.setEmisor(factory.createSolicitudRegistroClienteEmisor(emisor));
        solicitud.setRfcIntegrador(factory.createSolicitudRegistroClienteRfcIntegrador(RFC_Integrador_Pruebas));
        solicitud.setToken(factory.createSolicitudRegistroClienteToken(token));
        solicitud.setTransaccionID(trsId);

        // Registra el nuevo Emisor
        RespuestaRegistro respuesta;
        try {
            Clientes_Service servicio = new Clientes_Service();
            Clientes puerto = servicio.getPuertoClientes();
            respuesta = puerto.registrar(solicitud);
            infoEmisor = String.format("Estatus: %s\nNo. Certificado: %s", 
                                        respuesta.getRespuesta().getValue().getEstatus().getValue(),
                                        respuesta.getRespuesta().getValue().getClaveCertificado().getValue());
            
        } catch (ClientesRegistrarFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.clientes.FallaServicio falla = ex.getFaultInfo();
            infoEmisor = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (ClientesRegistrarFallaSesionFaultFaultMessage ex) {
            infoEmisor = ex.getMessage();
        } catch (ClientesRegistrarFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.clientes.FallaValidacion falla = ex.getFaultInfo();
            infoEmisor = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        }
        System.out.println(infoEmisor);
        
        return infoEmisor;
    }

    /**
     * Consulta la información de un Emisor: timbres asignados, restantes, utilizados y la fecha de caducidad
     * @param rfcEmisor RFC del Emisor a obtener
     * @return Una string conteniendo los timbres asignados y restantes y la fecha de caducidad
     */
    public String consultarEstatusEmisor(String rfcEmisor) {

        Long trsId = System.currentTimeMillis();        
        String timbresDisponibles;
        ecodex.servicios.clientes.ObjectFactory factory = new ecodex.servicios.clientes.ObjectFactory();

        // Obtenemos el token de seguridad para permitir la operación
        String token = obtenerToken(rfcEmisor, trsId);        
        
        // Crea la solicitud
        SolicitudEstatusCuenta solicitud = factory.createSolicitudEstatusCuenta();
        solicitud.setTransaccionID(trsId);
        solicitud.setToken(factory.createSolicitudEstatusCuentaToken(token));
        solicitud.setRFC(factory.createCuentaEstatusRFC(rfcEmisor));

        // Obtiene el estatus del Emisor
        RespuestaEstatusCuenta respuesta;
        try {
            Clientes_Service servicio = new Clientes_Service();
            Clientes puerto = servicio.getPuertoClientes();
            respuesta = puerto.estatusCuenta(solicitud);
            CuentaEstatus cuentaEstatus = respuesta.getEstatus().getValue();
            int utilizados = cuentaEstatus.getTimbresAsignados() - cuentaEstatus.getTimbresDisponibles();
            timbresDisponibles = String.format("Timbres asignados: %s\nTimbres restantes: %s\nTimbres utilizados: %s\nFecha Inicio: %s\nFecha Final: %s\n", 
                                                cuentaEstatus.getTimbresAsignados().toString(),
                                                cuentaEstatus.getTimbresDisponibles().toString(),
                                                utilizados,
                                                cuentaEstatus.getFechaInicio().toString(),
                                                cuentaEstatus.getFechaFin().toString());
            
        } catch (ClientesEstatusCuentaFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.clientes.FallaServicio falla = ex.getFaultInfo();
            timbresDisponibles = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (ClientesEstatusCuentaFallaSesionFaultFaultMessage ex) {
            timbresDisponibles = ex.getMessage();
        }
        
        System.out.println(timbresDisponibles );
        return timbresDisponibles;
    }
    
    /**
     * Asigna timbres a un Emisor
     * @param rfcEmisor RFC del Emisor al que se le asignarán los timbres
     * @param cantidadTimbres Cantidad de timbres a asignar
     * @return Una cadena conteniendo el nuevo saldo y saldo anterior 
     */
    public String asignarTimbresEmisor(String rfcEmisor, int cantidadTimbres){
        Long trsId = System.currentTimeMillis();        
        String saldos;
        ecodex.servicios.clientes.ObjectFactory factory = new ecodex.servicios.clientes.ObjectFactory();

        // Obtenemos el token de seguridad para permitir la operación
        String token = obtenerToken(rfcEmisor, trsId);        
        
        // Crea la solicitud
        SolicitudAsignacionTimbres solicitud = factory.createSolicitudAsignacionTimbres();
        solicitud.setRFC(factory.createSolicitudAsignacionTimbresRFC(rfcEmisor));
        solicitud.setTimbresAsignar(cantidadTimbres);
        solicitud.setTransaccionId(trsId);
        solicitud.setToken(factory.createSolicitudAsignacionTimbresToken(token));

        // Obtiene el estatus del Emisor
        ecodex.servicios.clientes.RespuestaAsignacionTimbres respuesta;
        try {
            Clientes_Service servicio = new Clientes_Service();
            Clientes puerto = servicio.getPuertoClientes();
            respuesta = puerto.asignacionTimbres(solicitud);
            saldos = String.format("Saldo Nuevo: %s\nSaldo Anterior: %s", respuesta.getSaldoNuevo(), respuesta.getSaldoAnterior());
            
        } catch (ClientesAsignacionTimbresFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.clientes.FallaServicio falla = ex.getFaultInfo();
            saldos = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (ClientesAsignacionTimbresFallaSesionFaultFaultMessage ex) {
            saldos = ex.getMessage();
        } catch (ClientesAsignacionTimbresFallaValidacionFaultFaultMessage ex) {
            ecodex.servicios.clientes.FallaValidacion falla = ex.getFaultInfo();
            saldos = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        }
        
        System.out.println(saldos);
        return saldos;        
    } 

    /**
     * Genera el token de seguridad para efectuar las funciones proporcionadas por los servicios
     * @param rfcEmisor RFC del Emisor que realiza la operación
     * @param trsId Id de la nueva transacción
     * @return El token generado.
     */
    public String obtenerToken(String rfcEmisor, Long trsId) {
 
        String token = obtenerTokenServicio(rfcEmisor, trsId);
        String ToHash = String.format("%s|%s", Integrador_ID, token);
        token = new Utils().toSHA1(ToHash.getBytes());
        
        return token;
    }

    /**
     * Genera el token de seguridad para efectuar el alta de emisores
     * @param trsId Id de la nueva transacción
     * @return El token generado.
     */
    public String obtenerTokenAltaEmisor(Long trsId) {
        
        String tokenAltaEmisor = obtenerTokenServicio(RFC_Integrador_Pruebas, trsId);
        String ToHash = String.format("%s|%s|%s", Integrador_ID, Alta_Emisores_ID, tokenAltaEmisor);
        tokenAltaEmisor = new Utils().toSHA1(ToHash.getBytes());            

        return tokenAltaEmisor;
    }

    /**
     * Genera un token de seguridad genérico
     * @param rfc
     * @param trsId
     * @return El token genérico.
     */
    public String obtenerTokenServicio(String rfc, Long trsId) {
        String token;

        // Obtenemos el factory para el servicio de seguridad (Token)
        ecodex.servicios.seguridad.ObjectFactory fac = new ecodex.servicios.seguridad.ObjectFactory();
        
        // Crea la solicitud para generar el tokenAltaEmisor
        SolicitudObtenerToken solicitudToken = fac.createSolicitudObtenerToken();
        solicitudToken.setTransaccionID(trsId);
        solicitudToken.setRFC(fac.createSolicitudObtenerTokenRFC(rfc));
       
        // Intenta obtener el tokenAltaEmisor
        RespuestaObtenerToken respuesta;
        try {
            Seguridad_Service servicio = new Seguridad_Service();
            Seguridad puerto = servicio.getPuertoSeguridad();            
            respuesta = puerto.obtenerToken(solicitudToken);
            token = respuesta.getToken().getValue();
            
        } catch (SeguridadObtenerTokenFallaServicioFaultFaultMessage ex) {
            ecodex.servicios.seguridad.FallaServicio falla = ex.getFaultInfo();
            token = String.format("%s, %s", falla.getNumero(), falla.getDescripcion().getValue());
        } catch (SeguridadObtenerTokenFallaSesionFaultFaultMessage ex) {
            token = ex.getMessage();
        }
        System.out.println("Token: " + token);
        
        return token;
    }
    
    /**
     * Obtiene el token/fecha de servicio concatenado mediante REST
     * @param rfcEmisor
     * @return
     * @throws ecodex.restapi.exceptions.FallaValidacion
     * @throws ecodex.restapi.exceptions.FallaSesion
     * @throws Exception 
     */
    public String obtenerTokenRest(String rfcEmisor) 
            throws ecodex.restapi.exceptions.FallaValidacion, ecodex.restapi.exceptions.FallaSesion, Exception {
        RestApi restApi = new RestApi();
        Token token = restApi.getToken(rfcEmisor, Integrador_ID);
        String fechaServicio = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(token.getService_datetime());
        return String.format("%s,%s", token.getAccess_token(), fechaServicio);
    }      
    
    /**
     * Servicio REST para recuperar un documento por Hash
     * @param rfcEmisor
     * @param hash
     * @return
     * @throws ecodex.restapi.exceptions.FallaValidacion
     * @throws ecodex.restapi.exceptions.FallaSesion
     * @throws Exception 
     */
    public String obtenerDocumentoPorHash(String rfcEmisor, String hash) 
            throws ecodex.restapi.exceptions.FallaValidacion, ecodex.restapi.exceptions.FallaSesion, Exception {
        String xml = new String();
        RestApi restApi = new RestApi();
        Token token = restApi.getToken(RFC_Emisor_Pruebas, Integrador_ID);
        xml = restApi.getDocumento(token, hash);
        return xml;
    }
    
    /**
     * Recupera una cadena con los ultimos timbres realizados mediante REST
     * @param rfcEmisor
     * @return
     * @throws ecodex.restapi.exceptions.FallaValidacion
     * @throws ecodex.restapi.exceptions.FallaSesion
     * @throws Exception 
     */
    public String obtenerUUIDsRecientes(String rfcEmisor) 
            throws ecodex.restapi.exceptions.FallaValidacion, ecodex.restapi.exceptions.FallaSesion, Exception {
        RestApi restApi = new RestApi();
        Token token = restApi.getToken(rfcEmisor, Integrador_ID);
        ArrayList<DocumentoInfo> documentos = restApi.getDocumentos(token);
        String listaString = "";
        
        listaString = documentos
                      .stream()
                      .map((documento) -> String.format("%s\r\n", documento.toString()))
                      .reduce(listaString, String::concat);        
        
        return listaString;
    }
    
    /**
     * Genera el token-url de carga de certificado mediante REST
     * @param rfcEmisor
     * @return
     * @throws ecodex.restapi.exceptions.FallaValidacion
     * @throws ecodex.restapi.exceptions.FallaSesion
     * @throws Exception 
     */
    public String obtenerUrlClaveCertificado(String rfcEmisor) 
            throws ecodex.restapi.exceptions.FallaValidacion, ecodex.restapi.exceptions.FallaSesion, Exception {
        RestApi restApi = new RestApi();
        Token token = restApi.getToken(rfcEmisor, Integrador_ID);
        CertificadoClave claveCertificado = restApi.getClaveCertificado(token);
        return String.format("%s?UUID=%s", Url_Carga_Certificados, claveCertificado.getClave());
    }    
}
